package com.springboot.mybatis.base.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.beans.ConstructorProperties;

public class SnowFlake {
    private static final Logger log = LoggerFactory.getLogger(SnowFlake.class);
    private static final Long START_TIME_STAMP = 1513602191879L;
    private static final Long SEQUENCE_BIT = 10L;
    private static final Long INSTANCE_BIT = 5L;
    private static final Long IDENTIFIER_BIT = 7L;
    private static final long MAX_INSTANCE_NUM;
    private static final long MAX_IDENTIFIER_NUM;
    private static final long MAX_SEQUENCE;
    private static final long IDENTIFIER_LEFT;
    private static final long INSTANCE_LEFT;
    private static final long TIMESTMP_LEFT;
    private Long instanceId;
    private Long identifier;
    private Long sequence;
    private Long lastTimeStamp;

    public synchronized long nextId() {
        Assert.isTrue(this.instanceId <= MAX_INSTANCE_NUM && this.instanceId >= 0L, "ID生成器实例值不符合要求！");
        Assert.isTrue(this.identifier <= MAX_IDENTIFIER_NUM && this.identifier >= 0L, "ID生成器业务标识不符合要求！");
        long currTimeStamp = this.getNow();
        Assert.isTrue(currTimeStamp >= this.lastTimeStamp, "时间错误！");
        if (currTimeStamp == this.lastTimeStamp) {
            this.sequence = this.sequence + 1L & MAX_SEQUENCE;
            if (this.sequence == 0L) {
                currTimeStamp = this.getNextMill();
            }
        } else {
            this.sequence = 0L;
        }

        this.lastTimeStamp = currTimeStamp;
        return currTimeStamp - START_TIME_STAMP << (int)TIMESTMP_LEFT | this.instanceId << (int)INSTANCE_LEFT | this.identifier << (int)IDENTIFIER_LEFT | this.sequence;
    }

    private long getNextMill() {
        long mill;
        for(mill = this.getNow(); mill <= this.lastTimeStamp; mill = this.getNow()) {
        }

        return mill;
    }

    private long getNow() {
        return System.currentTimeMillis();
    }

    private static Long $default$sequence() {
        return 0L;
    }

    private static Long $default$lastTimeStamp() {
        return -1L;
    }

    public static SnowFlake.SnowFlakeBuilder builder() {
        return new SnowFlake.SnowFlakeBuilder();
    }

    public Long getInstanceId() {
        return this.instanceId;
    }

    public Long getIdentifier() {
        return this.identifier;
    }

    public Long getSequence() {
        return this.sequence;
    }

    public Long getLastTimeStamp() {
        return this.lastTimeStamp;
    }

    public SnowFlake setInstanceId(Long instanceId) {
        this.instanceId = instanceId;
        return this;
    }

    public SnowFlake setIdentifier(Long identifier) {
        this.identifier = identifier;
        return this;
    }

    public SnowFlake setSequence(Long sequence) {
        this.sequence = sequence;
        return this;
    }

    public SnowFlake setLastTimeStamp(Long lastTimeStamp) {
        this.lastTimeStamp = lastTimeStamp;
        return this;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof SnowFlake)) {
            return false;
        } else {
            SnowFlake other = (SnowFlake)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label59: {
                    Object this$instanceId = this.getInstanceId();
                    Object other$instanceId = other.getInstanceId();
                    if (this$instanceId == null) {
                        if (other$instanceId == null) {
                            break label59;
                        }
                    } else if (this$instanceId.equals(other$instanceId)) {
                        break label59;
                    }

                    return false;
                }

                Object this$identifier = this.getIdentifier();
                Object other$identifier = other.getIdentifier();
                if (this$identifier == null) {
                    if (other$identifier != null) {
                        return false;
                    }
                } else if (!this$identifier.equals(other$identifier)) {
                    return false;
                }

                Object this$sequence = this.getSequence();
                Object other$sequence = other.getSequence();
                if (this$sequence == null) {
                    if (other$sequence != null) {
                        return false;
                    }
                } else if (!this$sequence.equals(other$sequence)) {
                    return false;
                }

                Object this$lastTimeStamp = this.getLastTimeStamp();
                Object other$lastTimeStamp = other.getLastTimeStamp();
                if (this$lastTimeStamp == null) {
                    if (other$lastTimeStamp != null) {
                        return false;
                    }
                } else if (!this$lastTimeStamp.equals(other$lastTimeStamp)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof SnowFlake;
    }

    @Override
    public int hashCode() {
        int result = 1;
        Object $instanceId = this.getInstanceId();
        result = result * 59 + ($instanceId == null ? 43 : $instanceId.hashCode());
        Object $identifier = this.getIdentifier();
        result = result * 59 + ($identifier == null ? 43 : $identifier.hashCode());
        Object $sequence = this.getSequence();
        result = result * 59 + ($sequence == null ? 43 : $sequence.hashCode());
        Object $lastTimeStamp = this.getLastTimeStamp();
        result = result * 59 + ($lastTimeStamp == null ? 43 : $lastTimeStamp.hashCode());
        return result;
    }

    @Override
    public String toString() {
        return "SnowFlake(instanceId=" + this.getInstanceId() + ", identifier=" + this.getIdentifier() + ", sequence=" + this.getSequence() + ", lastTimeStamp=" + this.getLastTimeStamp() + ")";
    }

    public SnowFlake() {
    }

    @ConstructorProperties({"instanceId", "identifier", "sequence", "lastTimeStamp"})
    public SnowFlake(Long instanceId, Long identifier, Long sequence, Long lastTimeStamp) {
        this.instanceId = instanceId;
        this.identifier = identifier;
        this.sequence = sequence;
        this.lastTimeStamp = lastTimeStamp;
    }

    static {
        MAX_INSTANCE_NUM = ~(-1L << INSTANCE_BIT);
        MAX_IDENTIFIER_NUM = ~(-1L << IDENTIFIER_BIT);
        MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
        IDENTIFIER_LEFT = SEQUENCE_BIT;
        INSTANCE_LEFT = IDENTIFIER_BIT + SEQUENCE_BIT;
        TIMESTMP_LEFT = INSTANCE_LEFT + INSTANCE_BIT;
    }

    public static class SnowFlakeBuilder {
        private Long instanceId;
        private Long identifier;
        private boolean sequence$set;
        private Long sequence;
        private boolean lastTimeStamp$set;
        private Long lastTimeStamp;

        SnowFlakeBuilder() {
        }

        public SnowFlake.SnowFlakeBuilder instanceId(Long instanceId) {
            this.instanceId = instanceId;
            return this;
        }

        public SnowFlake.SnowFlakeBuilder identifier(Long identifier) {
            this.identifier = identifier;
            return this;
        }

        public SnowFlake.SnowFlakeBuilder sequence(Long sequence) {
            this.sequence = sequence;
            this.sequence$set = true;
            return this;
        }

        public SnowFlake.SnowFlakeBuilder lastTimeStamp(Long lastTimeStamp) {
            this.lastTimeStamp = lastTimeStamp;
            this.lastTimeStamp$set = true;
            return this;
        }

        public SnowFlake build() {
            return new SnowFlake(this.instanceId, this.identifier, this.sequence$set ? this.sequence : SnowFlake.$default$sequence(), this.lastTimeStamp$set ? this.lastTimeStamp : SnowFlake.$default$lastTimeStamp());
        }
        @Override
        public String toString() {
            return "SnowFlake.SnowFlakeBuilder(instanceId=" + this.instanceId + ", identifier=" + this.identifier + ", sequence=" + this.sequence + ", lastTimeStamp=" + this.lastTimeStamp + ")";
        }
    }
}